/**
 * OKR 树形视图组件
 * 使用自定义的树形结构展示 OKR 层级关系
 *
 * ========== 布局算法流程图 ==========
 *
 * 1. 输入数据
 *    ├── nodes: Record<string, OKRNode>     // 所有节点数据
 *    ├── rootNodes: string[]               // 根节点ID列表
 *    └── collapsedNodes: string[]          // 折叠节点ID列表
 *
 * 2. 初始化阶段
 *    ├── positions = {}                    // 存储节点位置
 *    ├── connections = []                  // 存储连接线
 *    └── 设置尺寸常量 (280x120px + 间距)
 *
 * 3. 宽度计算阶段 (自底向上)
 *    └── calculateSubtreeWidth(nodeId)
 *        ├── 叶子节点: 280px
 *        ├── 折叠节点: 280px
 *        └── 展开节点: Σ(子节点宽度) + 间距
 *
 * 4. 位置布局阶段 (自顶向下)
 *    └── layoutNode(nodeId, x, y, level)
 *        ├── 计算节点居中位置: x + (子树宽度 - 280) / 2
 *        ├── 记录位置: positions[nodeId] = {x, y, width, height}
 *        ├── 递归布局子节点: 从左到右依次布局
 *        └── 生成连接线: 贝塞尔曲线连接父子节点
 *
 * 5. 输出结果
 *    ├── positions: 每个节点的精确位置
 *    ├── connections: 所有连接线的SVG路径
 *    └── treeSize: 整个画布的尺寸
 *
 * ========== 数据结构示例 ==========
 *
 * positions = {
 *   "DEPT-001": { x: 140, y: 50, width: 280, height: 120 },    // 部门节点位置
 *   "TEAM-001": { x: 50, y: 250, width: 280, height: 120 },   // 团队节点位置
 *   "PERSON-001-01-01": { x: 200, y: 450, width: 280, height: 120 } // 个人节点位置
 * }
 *
 * connections = [
 *   {
 *     from: "DEPT-001",           // 父节点：部门
 *     to: "TEAM-001",             // 子节点：团队
 *     path: "M 280 170 C 280 210 190 210 190 250"  // 贝塞尔曲线路径
 *   },
 *   {
 *     from: "TEAM-001",           // 父节点：团队
 *     to: "PERSON-001-01-01",     // 子节点：个人
 *     path: "M 190 370 C 190 410 340 410 340 450"  // 贝塞尔曲线路径
 *   }
 * ]
 *
 * ========== 算法复杂度 ==========
 *
 * 时间复杂度: O(n) - 每个节点访问一次
 * 空间复杂度: O(n) - 存储所有节点位置和连接线
 * 其中 n 为节点总数
 */

import React, { useRef, useMemo, useState, useCallback, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import type { OKRNode } from "../../../types/okr";
import type { RootState } from "../../../store";
import { toggleNodeCollapse } from "../../../store/okrSlice";
import { useVirtualizedRendering } from "../hooks/useVirtualizedRendering";
import VirtualizedNodeRenderer from "./VirtualizedNodeRenderer";
import "./OKRTreeView.css";

interface OKRTreeViewProps {
  /** 所有节点的映射表 */
  nodes: Record<string, OKRNode>;
  /** 根节点ID数组 */
  rootNodes: string[];
  /** 节点点击回调 */
  onNodeClick: (nodeId: string) => void;
}

export interface TreeLayout {
  /**
   * 节点位置信息映射表
   * key: 节点ID (如: "DEPT-001", "TEAM-001", "PERSON-001-01-01")
   * value: 节点的位置和尺寸信息
   */
  positions: Record<
    string,
    {
      x: number; // 节点在画布中的X坐标（左上角）
      y: number; // 节点在画布中的Y坐标（左上角）
      width: number; // 节点的宽度（像素）
      height: number; // 节点的高度（像素）
    }
  >;
  /**
   * 连接线路径信息数组
   * 用于绘制父子节点之间的连接线
   */
  connections: Array<{
    from: string; // 父节点ID（连接线起点节点）
    to: string; // 子节点ID（连接线终点节点）
    path: string; // SVG路径字符串（贝塞尔曲线，如: "M 140 120 C 140 160 140 160 140 200"）
  }>;
  /** 整个树的尺寸 */
  treeSize: { width: number; height: number };
}

/**
 * 计算树形布局（支持折叠）- 核心布局算法
 *
 * 算法步骤：
 * 1. 初始化布局参数和存储容器
 * 2. 递归计算每个节点的子树宽度（考虑折叠状态）
 * 3. 递归布局每个节点的位置（从上到下，从左到右）
 * 4. 生成父子节点间的连接线路径
 * 5. 返回完整的布局信息
 *
 * @param nodes 所有节点的映射表 { nodeId: OKRNode }
 * @param rootNodes 根节点ID数组 ["DEPT-001"]
 * @param collapsedNodes 折叠的节点ID数组 ["GROUP-001-01", "GROUP-002-01"]
 * @returns 布局信息 { positions, connections, treeSize }
 */

// 这个布局算法 不是单纯的深度优先遍历，而是：
// 第一阶段：后序遍历 - 计算子树宽度（从叶子向根）
// 第二阶段：前序遍历 - 确定节点位置（从根向叶子）
// 这种设计是因为：
// 宽度依赖：父节点宽度依赖子节点，需要后序遍历
// 位置依赖：子节点位置依赖父节点，需要前序遍历
const calculateTreeLayout = (
  nodes: Record<string, OKRNode>,
  rootNodes: string[],
  collapsedNodes: string[]
): TreeLayout => {
  // ========== 第1步：初始化布局参数和存储容器 ==========

  // 存储每个节点的位置信息 { nodeId: { x, y, width, height } }
  const positions: Record<string, { x: number; y: number; width: number; height: number }> = {};

  // 存储连接线信息 [{ from: parentId, to: childId, path: svgPath }]
  const connections: Array<{ from: string; to: string; path: string }> = [];

  // 节点尺寸配置常量
  const NODE_WIDTH = 280; // 每个节点的固定宽度
  const NODE_HEIGHT = 120; // 每个节点的固定高度
  const HORIZONTAL_SPACING = 100; // 同级节点间的水平间距
  const VERTICAL_SPACING = 80; // 不同层级间的垂直间距
  const LEVEL_HEIGHT = NODE_HEIGHT + VERTICAL_SPACING; // 每层的总高度

  // 布局计算过程中的临时变量
  let totalWidth = 0; // 整个树的总宽度

  let maxDepth = 0; // 树的最大深度（层级数）

  // ========== 第2步：递归计算每个节点的子树宽度（考虑折叠状态） ==========
  // 🌳 遍历方式：后序遍历（Post-order Traversal）
  // 特点：先访问所有子节点，再处理当前节点
  // 原因：父节点的宽度依赖于所有子节点的宽度，必须先计算子节点

  /**
   * 递归计算子树宽度 - 确定每个节点及其所有子孙节点占用的总宽度
   *
   * 计算逻辑：
   * 1. 如果节点不存在、无子节点或已折叠 → 返回单个节点宽度
   * 2. 如果有子节点且未折叠 → 递归计算所有子节点宽度之和 + 间距
   * 3. 返回 max(单节点宽度, 子树总宽度) 确保至少占用一个节点的空间
   *
   * @param nodeId 要计算的节点ID
   * @returns 该节点子树占用的总宽度（像素）
   *
   * 示例：
   * - 叶子节点: 280px (NODE_WIDTH)
   * - 有3个子节点的父节点: 280*3 + 100*2 = 1040px (子节点宽度 + 间距)
   */
  const calculateSubtreeWidth = (nodeId: string): number => {
    const node = nodes[nodeId];

    // 基础情况：无节点、无子节点、或已折叠 → 只占用单个节点的宽度
    if (!node || node.children.length === 0 || collapsedNodes.includes(nodeId)) {
      return NODE_WIDTH; // 280px
    }

    // 递归情况：计算所有子节点的宽度之和
    // 🔥 关键点：这里是递归计算，每个子节点的宽度包含了它的所有子孙节点
    //
    // 举例说明：
    // 部门A有3个团队：团队1、团队2、团队3
    // - 团队1下有2个小组，每个小组下有3个人 → 团队1子树宽度 = 2*(3*280+2*100)+100 = 1800px
    // - 团队2下有1个小组，小组下有5个人 → 团队2子树宽度 = 5*280+4*100 = 1800px
    // - 团队3下有3个小组，每个小组下有2个人 → 团队3子树宽度 = 3*(2*280+100)+200 = 2360px
    // 部门A的总宽度 = 1800 + 1800 + 2360 + 2*100 = 6160px
    //     1. 一次计算，全局布局
    // 递归计算确保每个节点都为其所有后代预留足够空间
    // 避免展开/折叠时的布局重排和重新计算
    // 2. 父节点完美居中
    // 父节点始终位于所有子孙节点的几何中心
    // 无论子树多复杂，视觉效果都很平衡
    // 布局稳定：展开/折叠不会影响其他节点位置

    let childrenWidth = 0;
    node.children.forEach(childId => {
      // 🌳 后序遍历的递归调用：
      // 1. 先递归计算子节点的宽度（会深入到叶子节点）
      // 2. 子节点计算完成后，累加到当前节点的宽度中
      // 3. 最后处理当前节点本身
      childrenWidth += calculateSubtreeWidth(childId);
    });

    // 算出 一层的 总节点宽度之后 + 上 节点 之间的间隙 返回

    // ========== 为什么不用"取最宽层宽度"的方案？==========
    //
    // 🤔 你的问题很好！确实有另一种算法：计算每一层的宽度，取最宽的那一层
    // 但我们选择"累加子树宽度"有以下关键原因：
    //
    // 1. 🔄 折叠状态动态处理
    // 当团队1折叠时：
    // - 方案A（当前）：团队1宽度变为280px，部门A自动重新计算为较小宽度
    // - 方案B（最宽层）：仍然按最深层计算，即使折叠了也占用大量空间
    //
    // 2. 🎯 局部布局独立性
    // - 方案A：每个子树的宽度只取决于自己的后代，互不影响
    // - 方案B：一个分支的深度会影响整个树的布局，耦合度高
    //
    // 3. 📐 精确空间利用
    // 假设团队2下只有1个人，团队3下有100个人：
    // - 方案A：团队2占用280px，团队3占用合理宽度，空间利用精确
    // - 方案B：团队2也要占用和团队3一样的宽度，浪费大量空间
    //
    // 4. 🚀 算法复杂度
    // - 方案A：O(n) 每个节点访问一次
    // - 方案B：O(n*d) 需要先遍历计算每层宽度，再比较，d为深度

    // 计算子节点间的间距总宽度
    // 例：3个子节点需要2个间距 → (3-1) * 100 = 200px
    const spacingWidth = (node.children.length - 1) * HORIZONTAL_SPACING;

    // 返回较大值：确保至少占用一个节点的宽度
    return Math.max(NODE_WIDTH, childrenWidth + spacingWidth);
  };

  // ========== 第3步：递归布局每个节点的位置（从上到下，从左到右） ==========
  // 🌳 遍历方式：前序遍历（Pre-order Traversal）
  // 特点：先处理当前节点，再处理所有子节点
  // 原因：父节点位置确定后，子节点才能根据父节点位置进行布局

  /**
   * 递归布局节点 - 计算每个节点的精确位置并生成连接线
   *
   * 布局逻辑：
   * 1. 计算当前节点的子树宽度
   * 2. 将当前节点居中放置在子树宽度范围内
   * 3. 记录节点位置到 positions 对象
   * 4. 如果有子节点且未折叠，递归布局所有子节点
   * 5. 为每个父子关系生成贝塞尔曲线连接线
   *
   * @param nodeId 要布局的节点ID
   * @param x 子树的起始X坐标（左边界）
   * @param y 当前层级的Y坐标
   * @param level 当前层级深度（0为根节点）
   * @returns 该节点子树实际占用的宽度
   *
   * 坐标系说明：
   * - 原点(0,0)在左上角
   * - X轴向右为正，Y轴向下为正
   * - 每层Y坐标 = level * LEVEL_HEIGHT
   */
  const layoutNode = (nodeId: string, x: number, y: number, level: number): number => {
    const node = nodes[nodeId];
    if (!node) return 0; // 节点不存在，返回0宽度

    // 更新树的最大深度
    maxDepth = Math.max(maxDepth, level);

    // 为什么要去 计算子节点的 总宽度 因为需要 在节点的中间
    // 获取当前节点的子树总宽度
    const subtreeWidth = calculateSubtreeWidth(nodeId);

    // 计算当前节点的X坐标：在子树宽度范围内居中
    // 例：子树宽度1040px，节点宽度280px → 居中偏移 (1040-280)/2 = 380px
    const nodeX = x + (subtreeWidth - NODE_WIDTH) / 2;

    // 🎯 前序遍历：先处理当前节点
    // 记录节点位置信息到 positions 对象中
    // 这个位置信息后续会用于渲染节点和虚拟化计算
    positions[nodeId] = {
      x: nodeX, // 节点左上角X坐标
      y: y, // 节点左上角Y坐标
      width: NODE_WIDTH, // 节点宽度 280px
      height: NODE_HEIGHT // 节点高度 120px
    };

    // ========== 第4步：处理子节点布局和连接线生成 ==========

    // 🌳 前序遍历：再处理所有子节点
    // 只有在节点有子节点且未被折叠时才进行子节点布局
    if (node.children.length > 0 && !collapsedNodes.includes(nodeId)) {
      let childX = x; // 子节点的起始X坐标（从父节点子树的左边界开始）
      const childY = y + LEVEL_HEIGHT; // 子节点的Y坐标（下一层级）

      // 遍历所有子节点，依次从左到右布局
      node.children.forEach(childId => {
        // 获取当前子节点的子树宽度
        const childSubtreeWidth = calculateSubtreeWidth(childId);

        // 🌳 前序遍历的递归调用：
        // 1. 当前节点已处理完毕（位置已记录）
        // 2. 现在递归处理子节点（子节点会先处理自己，再处理它的子节点）
        layoutNode(childId, childX, childY, level + 1);

        // ========== 生成父子节点间的连接线 ==========
        const childPos = positions[childId];
        if (childPos) {
          // 计算连接线的起点和终点坐标
          const parentCenterX = nodeX + NODE_WIDTH / 2; // 父节点底部中心X坐标
          const parentBottomY = y + NODE_HEIGHT; // 父节点底部Y坐标
          const childCenterX = childPos.x + NODE_WIDTH / 2; // 子节点顶部中心X坐标
          const childTopY = childPos.y; // 子节点顶部Y坐标

          // 创建贝塞尔曲线路径 - 生成平滑的S形连接线
          // 控制点位于起点和终点的中间高度，使连接线呈现优美的弧形
          const midY = parentBottomY + (childTopY - parentBottomY) / 2;
          const path = `M ${parentCenterX} ${parentBottomY}
                        C ${parentCenterX} ${midY}
                          ${childCenterX} ${midY}
                          ${childCenterX} ${childTopY}`;

          // 将连接线信息添加到 connections 数组中
          // 这些连接线后续会被SVG渲染引擎绘制
          connections.push({
            from: nodeId, // 父节点ID（如: "TEAM-001"）
            to: childId, // 子节点ID（如: "GROUP-001-01"）
            path // SVG贝塞尔曲线路径字符串
          });
        }

        // 移动到下一个子节点的起始位置
        // 当前子节点宽度 + 节点间距 = 下一个子节点的起始X坐标
        childX += childSubtreeWidth + HORIZONTAL_SPACING;
      });
    }

    return subtreeWidth;
  };

  // ========== 第5步：布局所有根节点并计算整体尺寸 ==========

  let currentX = 50; // 画布左侧留50px边距，避免节点贴边

  // 遍历所有根节点，从左到右依次布局
  rootNodes.forEach(rootId => {
    // 布局当前根节点及其所有后代节点
    const width = layoutNode(rootId, currentX, 50, 0); // Y坐标从50px开始，留出顶部边距

    // 计算下一个根节点的起始位置
    // 根节点间的间距是普通节点间距的2倍，增加视觉分离度
    currentX += width + HORIZONTAL_SPACING * 2;
    totalWidth = currentX; // 记录总宽度
  });

  // ========== 第6步：返回完整的布局信息 ==========
  return {
    positions, // 所有节点的位置信息映射表
    connections, // 所有连接线的路径信息数组
    treeSize: {
      // 整个树形图的画布尺寸
      width: Math.max(totalWidth, 800), // 最小宽度800px，实际宽度取决于节点数量
      height: (maxDepth + 1) * LEVEL_HEIGHT + 100 // 高度 = 层数 × 层高 + 底部边距100px
    }
  };
};

/**
 * OKR 树形视图组件
 */
const OKRTreeView: React.FC<OKRTreeViewProps> = ({ nodes, rootNodes, onNodeClick }) => {
  const dispatch = useDispatch();
  const containerRef = useRef<HTMLDivElement>(null);
  const contentRef = useRef<HTMLDivElement>(null);

  // 获取当前悬浮的节点ID和折叠状态
  const hoveredNodeId = useSelector((state: RootState) => state.okr.hoveredNodeId);
  // collapsedNodes（折叠节点id列表）
  const collapsedNodes = useSelector((state: RootState) => state.okr.collapsedNodes);

  // 缩放和平移状态
  const [scale, setScale] = useState(1);
  const [translate, setTranslate] = useState({ x: 0, y: 0 });
  const [isDragging, setIsDragging] = useState(false);
  const [lastMousePos, setLastMousePos] = useState({ x: 0, y: 0 });

  // 计算树形布局 - 使用 useMemo 缓存布局计算结果，避免重复计算
  // 当 nodes（节点数据）、rootNodes（根节点列表）、collapsedNodes（折叠节点列表）发生变化时才重新计算
  const layout = useMemo(() => {
    return calculateTreeLayout(nodes, rootNodes, collapsedNodes);
  }, [nodes, rootNodes, collapsedNodes]);

  // ========== 虚拟化渲染集成 ==========
  // 使用虚拟化渲染Hook来优化大规模节点的渲染性能
  // 只渲染当前可见区域内的节点，大幅提升性能和用户体验
  const { visibleNodes, renderStats } = useVirtualizedRendering(
    nodes, // 所有节点数据
    layout, // 布局信息
    containerRef, // 容器引用
    scale, // 缩放比例
    translate // 平移偏移
  );

  /**
   * 处理鼠标进入节点
   * @param nodeId 节点ID
   */
  // const handleNodeMouseEnter = useCallback(
  //   (nodeId: string) => {
  //     dispatch(setHoveredNode(nodeId));
  //   },
  //   [dispatch]
  // );

  /**
   * 处理鼠标离开节点
   */
  // const handleNodeMouseLeave = useCallback(() => {
  //   dispatch(setHoveredNode(undefined));
  // }, [dispatch]);

  /**
   * 处理节点点击
   * @param nodeId 节点ID
   */
  const handleNodeClick = useCallback(
    (nodeId: string) => {
      onNodeClick(nodeId);
    },
    [onNodeClick]
  );

  /**
   * 处理折叠切换
   * @param nodeId 节点ID
   */
  const handleToggleCollapse = useCallback(
    (nodeId: string) => {
      dispatch(toggleNodeCollapse(nodeId));
    },
    [dispatch]
  );

  /**
   * 处理鼠标滚轮缩放
   */
  const handleWheel = useCallback(
    (e: WheelEvent) => {
      e.preventDefault();

      const delta = e.deltaY > 0 ? 0.9 : 1.1;
      const newScale = Math.min(Math.max(scale * delta, 0.1), 3);

      if (containerRef.current) {
        const rect = containerRef.current.getBoundingClientRect();
        const mouseX = e.clientX - rect.left;
        const mouseY = e.clientY - rect.top;

        // 计算以鼠标为中心的缩放
        const scaleRatio = newScale / scale;
        const newTranslateX = mouseX - (mouseX - translate.x) * scaleRatio;
        const newTranslateY = mouseY - (mouseY - translate.y) * scaleRatio;

        setScale(newScale);
        setTranslate({ x: newTranslateX, y: newTranslateY });
      }
    },
    [scale, translate]
  );

  /**
   * 处理鼠标按下开始拖拽 拖拽画布
   */
  const handleMouseDown = useCallback((e: React.MouseEvent) => {
    // 只有在空白区域才开始拖拽
    if (e.target === containerRef.current || e.target === contentRef.current) {
      setIsDragging(true);
      // 设置鼠标 按下的位置
      setLastMousePos({ x: e.clientX, y: e.clientY });
    }
  }, []);

  /**
   * 处理鼠标移动拖拽
   */
  const handleMouseMove = useCallback(
    (e: MouseEvent) => {
      if (isDragging) {
        const deltaX = e.clientX - lastMousePos.x;
        const deltaY = e.clientY - lastMousePos.y;

        // 这是 需要 平移的 尺寸大小
        setTranslate(prev => ({
          x: prev.x + deltaX,
          y: prev.y + deltaY
        }));

        setLastMousePos({ x: e.clientX, y: e.clientY });
      }
    },
    [isDragging, lastMousePos]
  );

  /**
   * 处理鼠标抬起结束拖拽
   */
  const handleMouseUp = useCallback(() => {
    setIsDragging(false);
  }, []);

  /**
   * 重置视图到初始状态
   */
  const resetView = useCallback(() => {
    setScale(1);
    setTranslate({ x: 0, y: 0 });
  }, []);

  // 绑定事件监听器
  // 这种鼠标滚轮 事件 用来 缩放画布
  useEffect(() => {
    const container = containerRef.current;
    if (container) {
      container.addEventListener("wheel", handleWheel, { passive: false });
      return () => container.removeEventListener("wheel", handleWheel);
    }
  }, [handleWheel]);

  useEffect(() => {
    if (isDragging) {
      // 点击了 画布，就给 document 添加鼠标移动和鼠标抬起事件
      document.addEventListener("mousemove", handleMouseMove);
      document.addEventListener("mouseup", handleMouseUp);
      return () => {
        document.removeEventListener("mousemove", handleMouseMove);
        document.removeEventListener("mouseup", handleMouseUp);
      };
    }
  }, [isDragging, handleMouseMove, handleMouseUp]);

  return (
    <div
      ref={containerRef}
      className="okr-tree-container"
      style={{
        width: "100%",
        height: "100%",
        overflow: "hidden",
        position: "relative",
        cursor: isDragging ? "grabbing" : "grab"
      }}
      onMouseDown={handleMouseDown}
    >
      {/* ========== 性能监控面板（开发环境） ========== */}
      {import.meta.env.DEV && (
        <div
          className="performance-monitor"
          style={{
            position: "absolute",
            top: "10px",
            left: "10px",
            zIndex: 100,
            background: "rgba(0,0,0,0.9)",
            color: "white",
            borderRadius: "6px",
            boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
            padding: "12px",
            fontSize: "11px",
            fontFamily: "monospace",
            minWidth: "200px",
            backdropFilter: "blur(8px)"
          }}
        >
          <div style={{ fontWeight: "bold", marginBottom: "8px", color: "#4ade80" }}>🚀 虚拟化渲染统计</div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "4px", fontSize: "10px" }}>
            <div>📊 总节点:</div>
            <div style={{ color: "#fbbf24" }}>{renderStats.totalNodes}</div>

            <div>👁️ 可见节点:</div>
            <div style={{ color: "#34d399" }}>{renderStats.visibleNodes}</div>

            <div>⚡ 渲染效率:</div>
            <div style={{ color: "#60a5fa" }}>{renderStats.efficiency}</div>

            <div>🎯 性能提升:</div>
            <div style={{ color: "#f472b6" }}>{renderStats.performanceGain}</div>

            <div>💾 内存节省:</div>
            <div style={{ color: "#a78bfa" }}>{renderStats.memorySaved}</div>

            <div>⏱️ 计算耗时:</div>
            <div
              style={{
                color: renderStats.renderTime < 5 ? "#10b981" : renderStats.renderTime < 16 ? "#f59e0b" : "#ef4444"
              }}
            >
              {renderStats.renderTime.toFixed(2)}ms
            </div>
          </div>

          {/* 性能等级指示器 */}
          <div
            style={{
              marginTop: "8px",
              padding: "4px 8px",
              borderRadius: "4px",
              fontSize: "10px",
              textAlign: "center",
              background:
                renderStats.renderTime < 1
                  ? "#065f46"
                  : renderStats.renderTime < 5
                    ? "#1f2937"
                    : renderStats.renderTime < 16
                      ? "#451a03"
                      : "#7f1d1d"
            }}
          >
            {renderStats.renderTime < 1
              ? "🚀 极快"
              : renderStats.renderTime < 5
                ? "⚡ 快速"
                : renderStats.renderTime < 16
                  ? "✅ 良好"
                  : "⚠️ 需优化"}
          </div>
        </div>
      )}

      {/* 缩放控制工具栏 */}
      <div
        className="zoom-controls"
        style={{
          position: "absolute",
          top: "10px",
          right: "10px",
          zIndex: 100,
          background: "white",
          borderRadius: "4px",
          boxShadow: "0 2px 8px rgba(0,0,0,0.1)",
          padding: "4px",
          display: "flex",
          gap: "4px"
        }}
      >
        <button
          onClick={() => setScale(prev => Math.min(prev * 1.2, 3))}
          style={{
            border: "1px solid #d9d9d9",
            background: "white",
            padding: "4px 8px",
            cursor: "pointer",
            borderRadius: "2px"
          }}
        >
          +
        </button>
        <button
          onClick={() => setScale(prev => Math.max(prev * 0.8, 0.1))}
          style={{
            border: "1px solid #d9d9d9",
            background: "white",
            padding: "4px 8px",
            cursor: "pointer",
            borderRadius: "2px"
          }}
        >
          -
        </button>
        <button
          onClick={resetView}
          style={{
            border: "1px solid #d9d9d9",
            background: "white",
            padding: "4px 8px",
            cursor: "pointer",
            borderRadius: "2px",
            fontSize: "12px"
          }}
        >
          重置
        </button>
        <span
          style={{
            padding: "4px 8px",
            fontSize: "12px",
            color: "#666",
            minWidth: "40px",
            textAlign: "center"
          }}
        >
          {Math.round(scale * 100)}%
        </span>
      </div>

      {/* 树 整个布局  */}
      <div
        ref={contentRef}
        className="okr-tree-content"
        style={{
          width: layout.treeSize.width,
          height: layout.treeSize.height,
          position: "relative",
          transform: `translate(${translate.x}px, ${translate.y}px) scale(${scale})`,
          transformOrigin: "0 0",
          transition: isDragging ? "none" : "transform 0.1s ease-out"
        }}
      >
        {/* ========== 虚拟化渲染区域 ========== */}
        {/*
          使用虚拟化渲染器来优化大规模节点的渲染性能
          - 只渲染可见区域内的节点，减少DOM元素数量
          - 智能缓存已渲染的节点，避免重复渲染
          - 自动处理连接线的过滤和渲染
        */}
        <VirtualizedNodeRenderer
          visibleNodes={visibleNodes}
          nodes={nodes}
          layout={layout}
          hoveredNodeId={hoveredNodeId}
          collapsedNodes={collapsedNodes}
          onNodeClick={handleNodeClick}
          onToggleCollapse={handleToggleCollapse}
        />

        {/* 未使用虚拟渲染的实现 */}
        {/* 节点和 连线是 分开渲染的 */}

        {/* 渲染连接线 */}
        {/* <svg
          className="okr-tree-connections"
          style={{
            position: "absolute",
            top: 0,
            left: 0,
            width: "100%",
            height: "100%",
            pointerEvents: "none",
            zIndex: 1
          }}
        >
          <defs>
            箭头标记
            <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
              <polygon points="0 0, 10 3.5, 0 7" fill="#d1d5db" />
            </marker>
          </defs>

          渲染连接线 - 遍历 connections 数组绘制父子节点间的连接线
          {layout.connections.map((connection, index) => (
            <path
              key={`${connection.from}-${connection.to}-${index}`}
              d={connection.path} // 使用 connection.path 作为SVG路径
              stroke="#d1d5db"
              strokeWidth="2"
              fill="none"
              markerEnd="url(#arrowhead)"
              className="okr-connection-line"
            />
          ))}
        </svg> */}

        {/* 渲染节点 - 遍历 positions 对象来确定每个节点的位置 */}
        {/* {Object.entries(layout.positions).map(([nodeId, position]) => {
          const node = nodes[nodeId];
          if (!node) return null;

          return (
            <div
              key={nodeId}
              style={{
                position: "absolute",
                left: position.x, // 使用 position.x 设置节点的左边距
                top: position.y, // 使用 position.y 设置节点的上边距
                width: position.width, // 使用 position.width 设置节点宽度
                height: position.height, // 使用 position.height 设置节点高度
                zIndex: 2
              }}
              onMouseEnter={() => handleNodeMouseEnter(nodeId)}
              onMouseLeave={handleNodeMouseLeave}
              onClick={e => {
                e.stopPropagation();
                handleNodeClick(nodeId);
              }}
            >
              <OKRNodeComponent
                node={node}
                isHovered={hoveredNodeId === nodeId}
                isCollapsed={collapsedNodes.includes(nodeId)}
                onToggleCollapse={handleToggleCollapse}
              />
            </div>
          );
        })} */}
      </div>
    </div>
  );
};

export default OKRTreeView;
